﻿#include "usbbox.h"
#include "QFile"

UsbBox::UsbBox(Serial*serial, QObject *parent) : QObject(parent)
{
    this->serial = serial;
    this->address = 0; //usb box address is 0

    work_thread = new QThread();
    this->moveToThread(work_thread);

    connect(this, &UsbBox::UpdateFwSignal, this, [this](QString name, int dev_addr) {
        QFile *file = new QFile(name);
        file->open(QFile::ReadOnly);
        QByteArray data = file->readAll();
        delete file;
        UpdateFirmWare(data, dev_addr);
    });
    work_thread->start();
}

bool UsbBox::GetVersion(QString &version, int dev_addr){
    Command command(Enum::CMD_F8, dev_addr);
    int devStatus = 0;
    GetDevStatus(devStatus, dev_addr);
    u32 flash_addr;
    if (!device_in_boot(devStatus)) { //device is in app
        flash_addr = 0x2A00;
    }else { //device is in bootloader mode
        flash_addr = 0x0A00;
    }
    command.AppendValue24(flash_addr);
    qDebug()<< QDateTime::currentDateTimeUtc() <<", read version = " << flash_addr << ", " << dev_addr;
    if (!command.with_serial(serial)->sendSync()) {
        return false;
    }
    RxMessage *rsp = command.get_response();
    if (rsp->remain() < 3) {
        return false;
    }
    if (flash_addr != rsp->value_u24()) {
        return false;
    }
    version = QString(QByteArray(rsp->buffer() + rsp->offset(), rsp->remain()));
    return true;
}

bool UsbBox::ResetDevice(int devaddr){
    Command command(Enum::CMD_FD, devaddr);
    command.with_serial(serial)->with_retry(1)->sendSync();
    return true;
}

bool UsbBox::GetDevStatus(int &status, int dev_addr){
    Command command(Enum::CMD_F6, dev_addr);
    if (!command.with_serial(serial)->with_retry(5)->sendSync()) {
        qDebug() << "send F6 error";
        return false;
    }
    RxMessage *rsp = command.get_response();
    if (rsp->remain() < 1) {
        qDebug() << "F6 len = " << rsp->remain();
        return false;
    }

    status = rsp->value_u8();

    qDebug() <<"F6 stauts=" << status;
    return true;
}

void UsbBox::UpdateFWNotify(QString s, int percent){
    emit UpdateNotifySignal(s, percent);
}

bool UsbBox::UpdateFirmWare(QByteArray &data, int devaddr){
    u32 crc32 = Utils::get_crc32((const u8 *)data.toStdString().data(), data.size());
    qDebug() << "Update addr=" << devaddr;
    Command *command = new Command(Enum::CMD_F0, devaddr);
    UpdateFWNotify("进入升级模式", 0);
    command->with_timeout(200)->with_retry(20)->with_serial(serial);
    if (!command->sendSync()) {
        UpdateFWNotify("进入升级模式失败", -1);
        return false;
    }
    delete command;
    command = new Command(Enum::CMD_F1, devaddr);
    command->AppendValue24(data.size());
    command->with_serial(serial);
    if (!command->sendSync()) {
        UpdateFWNotify("开始下载失败", -1);
        return false;
    }
    delete command;
    int max_len = 128;
    int position = 0;
    u8 *buff = (u8 *)data.data();
    int length = data.size();
    int percent;
    while(position < length) {
        int wlen = (length - position)>max_len?max_len:(length - position);
        int next_pos = position + wlen;
        percent = position *100 / length;
        int times = 0;
        while(true){
            command = new Command(Enum::CMD_F4, devaddr);
            command->with_timeout(3000)->with_retry(5);
            command->AppendValue24(position);
            command->AppendBytes(buff+position, wlen);
            if (!command->with_serial(serial)->sendSync()) {
                UpdateFWNotify("发送数据失败", -1);
                delete command;
                return false;
            }
            RxMessage *rsp = command->get_response();
            if (rsp->size() < 3) {
                UpdateFWNotify("发送数据应答长度错误", -1);
                delete command;
                return false;
            }
            int value = rsp->value_u24();
            //qDebug() << "value = " << value << ", pos=" << next_pos;
            if (value == next_pos) {
                delete command;
                break;
            }
            if (times++ > 10) {
                UpdateFWNotify("发送数据答偏移错误", -1);
                delete command;
                return false;
            }
            delete command;
        }
        position = next_pos;
        UpdateFWNotify("发送数据", percent);
    }
    percent = 100;
    UpdateFWNotify("发送数据", percent);
    command = new Command(Enum::CMD_F2, devaddr);
    command->with_timeout(500)->with_retry(20);
    command->AppendValue24(length);
    command->AppendValue32(crc32);
    if (!command->with_serial(serial)->sendSync()) {
        UpdateFWNotify("crc32校验错误", -1);
        return false;
    }
    delete command;
    DataCommand *f5_command = new DataCommand(Enum::CMD_F5, devaddr);
    f5_command->with_timeout(3000)->with_retry(5);
    f5_command->AppendValue32(crc32);
    while(true) {
        if (!f5_command->with_serial(serial)->sendSync()) {
            UpdateFWNotify("新固件没有响应", -1);
            return false;
        }
        int code = f5_command->get_response()->value_u8();
        qDebug() << "code = " << code;
        if (code == 0) {
            break;
        }
        if (code != 100) {
            UpdateFWNotify("启动新固件失败", -1);
            return false;
        }
    }
    if (f5_command->get_response()->remain() < 5) {
        UpdateFWNotify("升级成功", percent);
        return true;
    }
    u32 patten = f5_command->get_response()->value_u32();
    if (patten != crc32) {
        UpdateFWNotify("通信测试失败", -1);
        return false;
    }
    delete f5_command;
    command = new Command(Enum::CMD_F9, devaddr);
    if (!command->with_serial(serial)->sendSync()) {
        UpdateFWNotify("写升级标志失败", -1);
        return false;
    }
    UpdateFWNotify("升级成功", percent);
    return true;
}
